import os
import shutil
import tarfile
from git import Repo
import time
import argparse
import sys
import logging
import multiprocessing
from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED

logging.basicConfig(level = logging.INFO, format = '%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger()

def parse_download(yocto_path):
    with open(yocto_path+"/scripts/download_code.sh", 'r') as f:
        code_line_list = []
        for line in f.readlines():
            line = line.replace("\r", "").replace("\n", "").replace("\t", "").strip(" ")
            if line.startswith("update_code_repo "):
                if "$name" in line:
                    continue
                code_line_list.append(line)
            if line.startswith("SRC_BRANCH"):
                line_split = line.strip(" ").split("=")
                src_branch = line_split[1].replace("\"", "")
            if line.startswith("KERNEL_BRANCH"):
                line_split = line.strip(" ").split("=")
                kernel_branch = line_split[1].replace("\"", "")

        # for line_code in code_list:
        logger.info("SRC_BRANCH : {}".format(src_branch))
        logger.info("KERNEL_BRANCH : {}".format(kernel_branch))

        code_list = []
        for code_line in code_line_list:
            code_line_split = code_line.split(" ")
            if "SRC_BRANCH" in code_line_split[2]:
                branch = src_branch
            elif "KERNEL_BRANCH" in code_line_split[2]:
                branch = kernel_branch
            else:
                branch = code_line_split[2]
            
            if len(code_line_split) < 4:
                repo = code_line_split[1].split("/")[1]
            else:
                repo = code_line_split[3]

            code_list.append({"repo_name": repo, "branch": branch})

        return code_list
        
def untar(fname, dirs):
    """
    解压tar.gz文件
    :param fname: 压缩文件名
    :param dirs: 解压后的存放路径
    :return: bool
    """
    try:
        t = tarfile.open(fname)
        t.extractall(path = dirs)
        return True
    except Exception as e:
        logger.error(e)
        return False

def git_checkout(to_dir, repo_code):

    try:
        logger.info("now checkout {} with {} ...".format(repo_code["repo_name"], repo_code["branch"]))
        
        repo_dir = os.path.join(to_dir, repo_code["repo_name"])
        repo = Repo(repo_dir)   
        git = repo.git
        git.checkout(repo_code["branch"])

    
        remote_url = repo.remote('origin').url.replace("https://gitee.com/","").replace(".git","")
        commit = repo.head.commit.hexsha
        branch = repo.active_branch.name
        line = remote_url + " " + repo_code["repo_name"] + " " + commit + " " + branch + "\n"
        with open(os.path.join(to_dir, "code.list"), 'a+') as f:
            f.write(line)

        logger.info("now checkout {} with {} finished".format(repo_code["repo_name"], repo_code["branch"]))
    except Exception as e:
        logger.info(repo_code["repo_name"])
        logger.error(e)
  
    

def myrepo(yocto_path, to_dir):
    repo = Repo(yocto_path)
    try:
        remote_url = repo.remote('origin').url.replace("https://gitee.com/","").replace(".git","")
        commit = repo.head.commit.hexsha
        branch = repo.active_branch.name
        line = remote_url + " yocto-meta-openeuler " + commit + " " + branch + "\n"
        logger.info(line)

        with open(os.path.join(to_dir, "src", "code.list"), 'a+') as f:
            f.write(line)
    except Exception as e:
        logger.error(e)

    

def create_manifest(to_dir):

    if os.path.exists(os.path.join(to_dir,"manifest.xml")):
        os.remove(os.path.join(to_dir,"manifest.xml"))

    code_lines = []
    with open(os.path.join(to_dir, "code.list"), 'r') as f:
        for read_line in f.readlines():
            if read_line != "":
                code_lines.append(read_line.replace("\n","".replace("\r","")))

    with open(os.path.join(to_dir, 'manifest.xml'), 'a+') as f:
        f.write('<?xml version="1.0" encoding="utf-8"?>\n')
        f.write('<manifest>\n')
        f.write('    <remote name="gitee" fetch="https://gitee.com/" review="https://gitee.com/"/>\n')
        f.write('    <default revision="${SRC_BRANCH}" remote="gitee" sync-j="8"/>\n')

        for line in code_lines:
            line_split = line.split(" ")
            wline = "    <project name=\"{}.git\" path=\"{}\" revision=\"{}\" groups=\"openeuler\" upstream=\"{}\"/>".format(line_split[0],line_split[1],line_split[2],line_split[3])
            f.write(wline+"\n")

        f.write("</manifest>")

def init_args():
    """
    init args
    :return:
    """
    parser = argparse.ArgumentParser()
    parser.add_argument("-yocto_path", type=str, dest="yocto_path", default="yocto-meta-openeuler", required=False)
    parser.add_argument("-to_dir", type=str, dest="to_dir", default="openeuler", required=False)
    parser.add_argument("-tar_dir", type=str, dest="tar_dir", default="src.tar.gz", required=False)

    return parser.parse_args()


args = init_args()

# 检查是否存在download_sh.sh
if not os.path.exists(args.yocto_path):
    logger.info("please entry yocto-meta-openeuler path with param -yocto_path")
    sys.exit(0)

# 检查是否存在源码tar包
if not os.path.exists(args.tar_dir):
    logger.info("please entry src.tar.gz path with param -tar_dir")
    sys.exit(0)

# 如果目标路经不存在则创建
if not os.path.exists(args.to_dir):
    os.mkdir("source")

if os.path.exists(os.path.join(args.to_dir, "src")):
    logger.info("delete source directory")
    shutil.rmtree(os.path.join(args.to_dir, "src"))
    # os.rmdir("source")

repo_list = parse_download(args.yocto_path)

logger.info("untar src.tar.gz to source directory")
start_time = time.time()
untar(args.tar_dir, args.to_dir)
end_time = time.time()
logger.info("untar duration {}".format(end_time - start_time))

logger.info("now checkout branch from origin branch in source code")
os.mknod(os.path.join(args.to_dir, "src", "code.list"))
start_time = time.time()
cpu_count = multiprocessing.cpu_count()
with ThreadPoolExecutor(max_workers = cpu_count) as t:
    all_task = []
    for repo_code in repo_list:
        all_task.append(t.submit(git_checkout, os.path.join(args.to_dir, "src"),repo_code))
    wait(all_task, return_when = ALL_COMPLETED)
end_time = time.time()
logger.info("checkout branch from origin branch in source code finished duration {}".format(end_time - start_time))

myrepo(args.yocto_path, args.to_dir)

create_manifest(os.path.join(args.to_dir, "src"))